/*
 * FreeModbus Libary: user callback functions and buffer define in slave mode
 * Copyright (C) 2013 Armink <armink.ztl@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * File: $Id: user_mb_app.c,v 1.60 2013/11/23 11:49:05 Armink $
 */
#include "user_mb_app.h"
#include "board_conf.h"
#include "utils.h"

/*------------------------Slave mode use these variables----------------------*/
//Slave mode:DiscreteInputs variables

//Slave mode:Coils variables
uint8_t CoilBuf[2] = {0, 0};

//Slave mode:InputRegister variables

//Slave mode:HoldingRegister variables
extern device_parameters_t device_parameters;

/**
 * Modbus slave input register callback function.
 *
 * @param pucRegBuffer input register buffer
 * @param usAddress input register address
 * @param usNRegs input register number
 *
 * @return result
 */
eMBErrorCode eMBRegInputCB(uint8_t * pucRegBuffer, uint16_t usAddress, uint16_t usNRegs)
{
    eMBErrorCode    eStatus = MB_ENOERR;

    *pucRegBuffer++ = (uint8_t) (device_parameters.offset_addr >> 8);
    *pucRegBuffer++ = (uint8_t) (device_parameters.offset_addr & 0xFF);
    
    return eStatus;
}

/**
 * Modbus slave holding register callback function.
 *
 * @param pucRegBuffer holding register buffer
 * @param usAddress holding register address
 * @param usNRegs holding register number
 * @param eMode read or write
 *
 * @return result
 */
eMBErrorCode eMBRegHoldingCB(uint8_t * pucRegBuffer, uint16_t usAddress,
        uint16_t usNRegs, eMBRegisterMode eMode)
{
    eMBErrorCode    eStatus = MB_ENOERR;
    uint16_t        iRegIndex;
    uint16_t *      pusRegHoldingBuf = (uint16_t *)&device_parameters;

    /* it already plus one in modbus function method. */
    usAddress--;

    if ((usAddress >= S_REG_HOLDING_START)
            && (usAddress + usNRegs <= S_REG_HOLDING_START + S_REG_HOLDING_NREGS))
    {
        iRegIndex = usAddress - S_REG_HOLDING_START;
        switch (eMode)
        {
        /* read current register values from the protocol stack. */
        case MB_REG_READ:
            while (usNRegs > 0)
            {
                *pucRegBuffer++ = (uint8_t) (pusRegHoldingBuf[iRegIndex] >> 8);
                *pucRegBuffer++ = (uint8_t) (pusRegHoldingBuf[iRegIndex] & 0xFF);
                iRegIndex++;
                usNRegs--;
            }
            break;

        /* write current register values with new values from the protocol stack. */
        case MB_REG_WRITE:
            while (usNRegs > 0)
            {
                pusRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
                pusRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
                
                switch(iRegIndex) {
                case 0:
                    device_parameters.baudrate_opt = pusRegHoldingBuf[iRegIndex];
                    UART1_SetBaudrate(device_parameters.baudrate_opt);
                    save_parameters((void *)&device_parameters, PAR_BAUDRATE_OPT);
                    break;
                case 1:
                    device_parameters.rsv = pusRegHoldingBuf[iRegIndex];
                    break;
                case 2:
                    device_parameters.offset_addr = pusRegHoldingBuf[iRegIndex];
                    save_parameters((void *)&device_parameters, PAR_OFFSET_ADDR);
                    break;
                case 3:
                    if ((pusRegHoldingBuf[iRegIndex] < OM_SEQUENTIAL_ON) ||
                        (pusRegHoldingBuf[iRegIndex] > OM_OFF_ON)) {
                        break;
                    }
                    device_parameters.op_mode = pusRegHoldingBuf[iRegIndex];
                    save_parameters((void *)&device_parameters, PAR_OP_MODE);
                    break;
                case 4:
                    device_parameters.delay_time = pusRegHoldingBuf[iRegIndex];
                    save_parameters((void *)&device_parameters, PAR_DELAY_TIME);
                    break;
                default: break;
                }
                
                iRegIndex++;
                usNRegs--;
            }
            break;
        }
    }
    else
    {
        eStatus = MB_ENOREG;
    }
    return eStatus;
}

/**
 * Modbus slave coils callback function.
 *
 * @param pucRegBuffer coils buffer
 * @param usAddress coils address
 * @param usNCoils coils number
 * @param eMode read or write
 *
 * @return result
 */
eMBErrorCode eMBRegCoilsCB(uint8_t * pucRegBuffer, uint16_t usAddress,
        uint16_t usNCoils, eMBRegisterMode eMode)
{
    eMBErrorCode    eStatus = MB_ENOERR;
    uint16_t        iRegBitIndex;

    /* it already plus one in modbus function method. */
    usAddress--;

    if((usAddress >= S_COIL_START) &&
        (usAddress + usNCoils <= S_COIL_START + S_COIL_NCOILS))
    {
        iRegBitIndex = (uint16_t) (usAddress - S_COIL_START) % 8;
        switch (eMode)
        {
        /* read current coil values from the protocol stack. */
        case MB_REG_READ:
            
            read_coils(CoilBuf);
            
            if (usNCoils > 0) {
                *pucRegBuffer = xMBUtilGetBits(&CoilBuf[0],
                        iRegBitIndex, usNCoils);
            }
            /* filling zero to high bits */
            *pucRegBuffer = *pucRegBuffer << (8 - usNCoils);
            *pucRegBuffer = *pucRegBuffer >> (8 - usNCoils);
            break;

            /* write current coil values with new values from the protocol stack. */
        case MB_REG_WRITE:
            /* xMBUtilSetBits has bug when ucNBits is zero */
            if (usNCoils != 0)
            {
                xMBUtilSetBits(&CoilBuf[0], iRegBitIndex, usNCoils,
                        *pucRegBuffer);
                
                set_coils(&CoilBuf[0]);
            }
            break;
        }
    }
    else
    {
        eStatus = MB_ENOREG;
    }
    return eStatus;
}

/**
 * Modbus slave discrete callback function.
 *
 * @param pucRegBuffer discrete buffer
 * @param usAddress discrete address
 * @param usNDiscrete discrete number
 *
 * @return result
 */
eMBErrorCode eMBRegDiscreteCB(uint8_t * pucRegBuffer, uint16_t usAddress, uint16_t usNDiscrete)
{
    eMBErrorCode    eStatus = MB_ENOERR;
    uint16_t        iRegBitIndex;
    uint8_t         DiscreteInputBuf;
    
    /* it already plus one in modbus function method. */
    usAddress--;

    if ((usAddress >= S_DISCRETE_INPUT_START)
            && (usAddress + usNDiscrete    <= S_DISCRETE_INPUT_START + S_DISCRETE_INPUT_NDISCRETES))
    {
        iRegBitIndex = (uint16_t) (usAddress - S_DISCRETE_INPUT_START) % 8;

        read_discrete_inputs(&DiscreteInputBuf);
        
        *pucRegBuffer = xMBUtilGetBits(&DiscreteInputBuf, iRegBitIndex, usNDiscrete);

        /* filling zero to high bit */
        *pucRegBuffer = *pucRegBuffer << (8 - usNDiscrete);
        *pucRegBuffer = *pucRegBuffer >> (8 - usNDiscrete);
    }
    else
    {
        eStatus = MB_ENOREG;
    }

    return eStatus;
}


